//Copyright (C) 2010 Ron Coleman. Contact: ronncoleman@gmail.com
//
//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 3 of the License, or (at your option) any later version.
//
//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

package org.workplicity.cia.table;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import javax.swing.table.AbstractTableModel;
import org.workplicity.cia.util.CIAHelper;
import org.workplicity.elog.entry.Elog;
import org.workplicity.elog.entry.ElogUser;
import org.workplicity.elog.entry.Link;
import org.workplicity.elog.repos.Links;
import org.workplicity.util.DateFormatter;
import org.workplicity.util.Helper;
import org.workplicity.worklet.WorkletContext;

/**
 * This class implements the model for the links table on the
 * elog edit dialog.
 * @author Ron Coleman
 */
public class LinksTableModel extends AbstractTableModel {
    /**
     * Column names
     */
    private String[] columnNames = {
        "",
        "Id",
        "Update",
//        "Trade",
        "Log",
        "Description"
    };

    /**
     * This list contains logs that are already linked in the
     * repository.
     */
    private ArrayList<Elog> elogs = new ArrayList<Elog>( );

    /**
     * This list contains owners of logs that are linked and
     * in the repository
     */
//    private ArrayList<ElogUser> owners = new ArrayList<ElogUser>( );
    private HashMap<Elog,ElogUser> owners2 = new HashMap<Elog,ElogUser>( );

    /**
     * This hash contains the links that are in the repository.
     */
    private HashMap<Elog,Boolean> linkStates = new HashMap<Elog,Boolean>( );

    private HashMap<Elog,Link> elogLinks = new HashMap<Elog,Link>( );
    
    /**
     * This list holds the additions that the user has
     * manually added.
     */
    private ArrayList<Elog> additions = new ArrayList<Elog>( );

    public int getRowCount() {
        return elogs.size();
    }

    public int getColumnCount() {
        return columnNames.length;
    }

    @Override
    public boolean isCellEditable(int row, int col) {
        if(col == 0)
            return true;

        return false;
    }


    @Override
    public void setValueAt(Object value, int row, int col) {
        if(row < 0 || row >= elogs.size())
            return;

        Elog elog = elogs.get(row);

        linkStates.put(elog, (Boolean)value);

        fireTableCellUpdated(row, col);
    }

    /**
     * Gets the name for a column.
     * @param col Column number.
     * @return Column name
     */
    @Override
    public String getColumnName(int col) {
        return columnNames[col];
    }

    /*
     * JTable uses this method to determine the default renderer/
     * editor for each cell.  If we didn't implement this method,
     * then the last column would contain text ("true"/"false"),
     * rather than a check box.
     * See
     * http://download.oracle.com/javase/tutorial/uiswing/examples/components/TableDemoProject/src/components/TableDemo.java
     */
    @Override
    public Class getColumnClass(int c) {
        if(c == 0)
            return Boolean.TRUE.getClass();

        return String.class;
    }

    /**
     * Gets the value at row, col intersection
     * @param row Row
     * @param col Column
     * @return Object
     */
    public Object getValueAt(int row, int col) {
        if(row >= elogs.size())
            return null;

        Elog elog = elogs.get(row);

        if(col == 0)
            return linkStates.get(elog);

        if(col == 1)
            return elog.getId();

        if(col == 2)
            return DateFormatter.toString(elog.getUpdateDate());
        
        if(col == 3)
            return owners2.get(elog);

        if(col == 4)
            return CIAHelper.asOneLine(elog.getDescription());

        return null;
    }

    /**
     * Add a new link to the log.
     * @param elog Elog
     */
    public void add(Elog elog) {
        for(Elog alog : additions) {
            if(alog.getId().equals(elog.getId()) &&
                    alog.getReposTitle().equals(elog.getReposTitle()))
                return;
        }
        
        additions.add(elog);

        linkStates.put(elog, Boolean.TRUE);
    }

    /**
     * Gets the link states of all the logs.
     * @return Link states
     */
    public HashMap getLinkStates() {
        return linkStates;
    }

    public HashMap getElogLinks() {
        return elogLinks;
    }

    /**
     * Gets the link additions
     * @return Additions
     */
    public ArrayList<Elog> getAdditions() {
        return additions;
    }

    /**
     * Gets the elog in the row.
     * @param row Row index
     * @return Elog
     */
    public Elog getRow(int row) {
        if(row < 0 || row >= elogs.size())
            return null;

        return elogs.get(row);
    }

    /**
     * Gets the owner of this entry.
     * @param row Row index
     * @return Owner
     */
    public ElogUser getOwner(int row) {
        if(row < 0 || row >= elogs.size())
            return null;

        Elog elog = getRow(row);

        return owners2.get(elog);
    }

    /**
     * Refresh the links table
     * @param elog Elog
     */
    public void refresh(Elog elog) {
        refreshOldLinks(elog);

        refreshNewLinks();

        Collections.sort(elogs, new Comparator() {
            public int compare(Object o1, Object o2) {
                Date d1 = ((Elog) o1).getUpdateDate();
                Date d2 = ((Elog) o2).getUpdateDate();
                return d2.compareTo(d1);
            }
        });

        // Tell JTable we're added something
        this.fireTableDataChanged();
    }

    /**
     * Refresh the new links
     */
    private void refreshNewLinks() {
        // Add any additions
        // NOTE: the additions are already in the link states
        // hash map
        if (!additions.isEmpty()) {
            for (Elog alog : additions) {
                elogs.add(alog);

                String title = alog.getReposTitle();
                ElogUser user = CIAHelper.toUser(title);

                owners2.put(alog,user);
            }
        }
    }

    /**
     * Refresh all the old links unless this is a new
     * log entry in which case there can't be old links.
     * @param elog Elog
     */
    private void refreshOldLinks(Elog elog) {
        // Get the links that are pointing here or
        // those here that are pointing there
        
        // Note: we must get the "heres" and "theres" because 
        // we don't know if I created the link or some other user
        // created the link
        Integer here = elog.getId();
        
        if(here == -1)
            return;

        WorkletContext context = WorkletContext.getInstance();

        // No, here/here is not a bug. It says get me anything pointing
        // to this elog and anything this elog is point to
        String criteria = "/ list [ ( here = " + here + " and reposHere = '" + elog.getReposTitle() + "' ) or ";
        criteria += "( there = " + here + " and reposThere = '" + elog.getReposTitle() + "' ) ]";

        ArrayList<Link> links = Helper.query(Links.TITLE,criteria,context);

        // Get the elog except don't get this one!
        elogs.clear();
        owners2.clear();

        for(Link link : links) {
            // Skip a HERE that is pointing here
            if(!link.getHere().equals(here) && !link.getReposHere().equals(elog.getReposTitle())) {
                criteria = "/ list [ id = " + link.getHere() + " ]";

                Elog alog = (Elog) Helper.fetch(link.getReposHere(),link.getHere(),context);

                alog.setReposTitle(link.getReposHere());

                elogs.add(alog);

                ElogUser user = CIAHelper.toUser(link.getReposHere() );
                owners2.put(alog,user);

                if(!linkStates.containsKey(alog)) {
                    linkStates.put(alog, Boolean.TRUE);
                    elogLinks.put(alog, link);
                }
            }
//            if(!link.getThere().equals(here) && !link.getReposThere().equals(elog.getReposTitle())) {
            // If HERE is not pointing here then THERE must be point here
            else {
                criteria = "/ list [ id = " + link.getThere() + " ]";

                Elog alog = (Elog) Helper.fetch(link.getReposThere(),link.getThere(),context);

                alog.setReposTitle(link.getReposThere());

                elogs.add(alog);

//                ElogUser user = CIAHelper.toUser(link.getReposThere());
                ElogUser user = CIAHelper.toUser(link.getReposHere());
                owners2.put(alog,user);

                if(!linkStates.containsKey(alog)) {
                    linkStates.put(alog, Boolean.TRUE);
                    elogLinks.put(alog, link);
                }
            }
        }
    }
}
